package org.finesys.samples.netty.websocket.client;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.SSLException;

import org.finesys.common.netty.websocket.support.WebSocketEndpointConfig;
import org.springframework.beans.factory.DisposableBean;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

/**
 * WebSocket服务器管理类，创建websocket服务和优雅的关闭服务
 */
public class NettyWebSocketClient implements DisposableBean {
    private final WebSocketEndpointConfig webSocketEndpointConfig;
    //日志记录器，用于日志输出
    private static final InternalLogger INTERNAL_LOGGER = InternalLoggerFactory.getInstance(NettyWebSocketClient.class);
    // EventExecutorGroup配置
    private EventLoopGroup eventLoopGroup;


    public NettyWebSocketClient(WebSocketEndpointConfig webSocketEndpointConfig) {
        this.webSocketEndpointConfig = webSocketEndpointConfig;
    }

    /**
     * 初始化WebSocket服务器
     * <p>
     * 该方法负责初始化WebSocket服务器，包括配置SSL、跨域、EventExecutorGroup以及Boss和Worker的EventLoopGroup。
     *
     * @throws SSLException 如果SSL配置过程中出现异常
     */
    public void init() throws SSLException {
        // EventExecutorGroup配置
        if (webSocketEndpointConfig.isUseEventExecutorGroup()) {
            int threads = webSocketEndpointConfig.getEventExecutorGroupThreads();
            eventLoopGroup = new NioEventLoopGroup(threads > 0 ? threads : 16);
        } else {
            // 如果不使用EventExecutorGroup，则设置为null
            eventLoopGroup = new NioEventLoopGroup(16);
        }


        try {
            Bootstrap serverBootstrap;
            //启用stomp协议支持
            if ("wss".equals(webSocketEndpointConfig.getProtocol()) || "stomp".equals(webSocketEndpointConfig.getProtocol())) {
                serverBootstrap = initStompWebSocketServer(webSocketEndpointConfig, eventLoopGroup);
            } else {
                serverBootstrap = initWebSocketServer(webSocketEndpointConfig, eventLoopGroup);
            }
            //绑定到指定主机和端口，或者绑定到所有可用网络接口
            ChannelFuture channelFuture;

            try {
                // 绑定到指定主机和端口
                channelFuture = serverBootstrap.connect(new InetSocketAddress(InetAddress.getByName(webSocketEndpointConfig.getHost()), Integer.parseInt(webSocketEndpointConfig.getPort())));
            } catch (UnknownHostException e) {
                INTERNAL_LOGGER.error("Unknown host: {}", webSocketEndpointConfig.getHost(), e);
                // 如果主机名未知，则绑定到默认端口
                channelFuture = serverBootstrap.connect(new InetSocketAddress(Integer.parseInt(webSocketEndpointConfig.getPort())));
            }

            //发送一条消息到服务器，此处仅为示例
            channelFuture.channel().writeAndFlush("Hello, world!");
            // 添加监听器以处理绑定结果
            channelFuture.addListener(future -> {
                if (!future.isSuccess()) {
                    INTERNAL_LOGGER.error("Failed to connect server", future.cause());
                }
            });
            // 添加JVM关闭钩子以优雅关闭EventLoopGroup和EventExecutorGroup
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {

                if (eventLoopGroup != null) {
                    eventLoopGroup.shutdownGracefully().syncUninterruptibly();
                }
            }));
            // 同步等待服务器启动完成
            channelFuture.sync();
        } catch (Exception e) {
            INTERNAL_LOGGER.error("Client initialization failed", e);
        }
    }

    private Bootstrap initStompWebSocketServer(WebSocketEndpointConfig webSocketEndpointConfig, EventLoopGroup eventLoopGroup) throws SSLException {
        // 初始化WebSocket服务器，此处省略具体实现细节
        Bootstrap serverBootstrap = new Bootstrap();
        serverBootstrap.group(eventLoopGroup)
                .channel(NioSocketChannel.class)
                //Socket参数，连接保活，默认值为False。启用该功能时，TCP会主动探测空闲连接的有效性。可以将此功能视为TCP的心跳机制，需要注意的是：默认的心跳间隔是7200s即2小时。Netty默认关闭该功能
                .option(ChannelOption.SO_KEEPALIVE, webSocketEndpointConfig.isSoKeepalive())
                .handler(new StompClientInitializer());
        return serverBootstrap;
    }


    private Bootstrap initWebSocketServer(WebSocketEndpointConfig webSocketEndpointConfig, EventLoopGroup eventLoopGroup) throws SSLException {
        // 初始化WebSocket服务器，此处省略具体实现细节
        Bootstrap serverBootstrap = new Bootstrap();
        serverBootstrap.group(eventLoopGroup)
                .channel(NioSocketChannel.class)
                //Socket参数，连接保活，默认值为False。启用该功能时，TCP会主动探测空闲连接的有效性。可以将此功能视为TCP的心跳机制，需要注意的是：默认的心跳间隔是7200s即2小时。Netty默认关闭该功能
//                .option(ChannelOption.SO_KEEPALIVE, webSocketEndpointConfig.isSoKeepalive())
                //增加日志处理器，用于记录日志
                .handler(new StompClientHandler());
        //Socket参数，TCP数据接收缓冲区大小。该缓冲区即TCP接收滑动窗口，linux操作系统可使用命令：cat /proc/sys/net/ipv4/tcp_rmem查询其大小。一般情况下，该值可由用户在任意时刻设置，但当设置值超过64KB时，需要在连接到远端之前设置。
        if (webSocketEndpointConfig.getSoRcvbuf() != -1) {
            serverBootstrap.option(ChannelOption.SO_RCVBUF, webSocketEndpointConfig.getSoRcvbuf());
        }
        //Socket参数，TCP数据发送缓冲区大小。该缓冲区即TCP发送滑动窗口，linux操作系统可使用命令：cat /proc/sys/net/ipv4/tcp_smem查询其大小。
        if (webSocketEndpointConfig.getSoSndbuf() != -1) {
            serverBootstrap.option(ChannelOption.SO_SNDBUF, webSocketEndpointConfig.getSoSndbuf());
        }
        return serverBootstrap;
    }

    /**
     * 根据传入的日志级别字符串返回对应的LogLevel枚举值
     *
     * @param level 日志级别字符串，支持大小写混合
     * @return LogLevel枚举值，如果传入的级别字符串无效，则默认返回LogLevel.ERROR
     */
    private LogLevel logLevel(String level) {
        // 将日志级别字符串转换为大写
        level = level.toUpperCase();
        // 使用Map简化日志级别的匹配过程
        Map<String, LogLevel> levelMap = new HashMap<>();
        levelMap.put("TRACE", LogLevel.TRACE);
        levelMap.put("DEBUG", LogLevel.DEBUG);
        levelMap.put("INFO", LogLevel.INFO);
        levelMap.put("WARN", LogLevel.WARN);
        levelMap.put("ERROR", LogLevel.ERROR);

        // 返回对应的LogLevel，如果未找到则默认返回LogLevel.ERROR
        return levelMap.getOrDefault(level, LogLevel.ERROR);
    }


    /**
     * 销毁方法，优雅地关闭EventLoopGroup
     *
     * @throws Exception 抛出异常
     */
    @Override
    public void destroy() throws Exception {
        // 确保EventLoopGroup被优雅关闭
        if (eventLoopGroup != null && !eventLoopGroup.isShutdown()) {
            eventLoopGroup.shutdownGracefully();
        }
    }
}
